﻿using System;
using System.Collections.Generic; 
using System.Text;

namespace Titan.ExpressionAnalyse
{
    internal static class SqlAnalyzer
    {
        //internal class OutputColumnInfo
        //{
        //    public OutputColumnInfo()
        //    {
        //        this.GroupFunction = GroupFunction.None;
        //    }
        //    public OutputColumnInfo(GroupFunction groupFunction)
        //    {
        //        this.GroupFunction = groupFunction;
        //    }
        //    public GroupFunction GroupFunction { get; set; }


        //    public override int GetHashCode()
        //    {
        //        return this.GroupFunction.GetHashCode();
        //    }
        //    public override bool Equals(object obj)
        //    {
        //        return this.GroupFunction.Equals(obj);
        //    }
        //}
        //class OutputInfo
        //{
        //    public OutputInfo()
        //    {
        //        this.OutputColumnInfos = new SortedSet<OutputColumnInfo>();
        //    }
        //    bool IsOutput { get; set; }
        //    public SortedSet<OutputColumnInfo> OutputColumnInfos { get; set; }
        //}

        //缓存
        private static readonly object SqlAnalyseResultsLock = new object();
        internal static readonly Dictionary<string, SqlAnalyseResult> SqlAnalyseResults = new Dictionary<string, SqlAnalyseResult>(StringComparer.OrdinalIgnoreCase);

        /// <summary>
        /// 收集queryExpression中所有用的到属性名
        /// </summary>
        /// <param name="queryExpression"></param>
        /// <returns></returns>
        public static SqlAnalyseResult Analyse(ISqlProvider sqlProvider, EntityType entityType, IQueryExpression queryExpression)
        {
            //使用sorted是为了判断gethashcode时是否缓存中已经存在
            //在分析时taskPropertyNames只是判断是否要输出，是否有合计函数
            SortedDictionary<string, SortedDictionary<GroupFunction, bool>> taskPropertyNames = new SortedDictionary<string, SortedDictionary<GroupFunction, bool>>(StringComparer.OrdinalIgnoreCase);

            //必须先分析输出部分，因为输出部分包含是否要填充的信息
            CollectPropertyNames(taskPropertyNames, queryExpression.Selects, sqlProvider, entityType);
            CollectPropertyNames(taskPropertyNames, queryExpression.Wheres);
            CollectPropertyNames(taskPropertyNames, queryExpression.Orders);


            CollectPropertyNames(taskPropertyNames, queryExpression.Havings);
            CollectPropertyNames(taskPropertyNames, queryExpression.Groups);

            return Analyse(sqlProvider,entityType,taskPropertyNames);
        }
        public static SqlAnalyseResult Analyse(ISqlProvider sqlProvider, EntityType entityType, IUpdateExpression updateExpression)
        {
            //使用sorted是为了判断gethashcode时是否缓存中已经存在
            SortedDictionary<string, SortedDictionary<GroupFunction, bool>> taskPropertyNames = new SortedDictionary<string, SortedDictionary<GroupFunction, bool>>(StringComparer.OrdinalIgnoreCase);

            CollectPropertyNames(taskPropertyNames, updateExpression.Updates);
            CollectPropertyNames(taskPropertyNames, updateExpression.Wheres); 

            return Analyse(sqlProvider, entityType, taskPropertyNames);
        }

        public static SqlAnalyseResult Analyse(ISqlProvider sqlProvider, EntityType entityType, SortedDictionary<string, SortedDictionary<GroupFunction, bool>> taskPropertyNames)
        { 
            //注意不同实体类型可能有相同的字段因此必须传入entityType
            string hashKey = GetHashKey(entityType, taskPropertyNames);
            if (!SqlAnalyseResults.ContainsKey(hashKey))
            {
                lock (SqlAnalyseResultsLock)
                {
                    if (!SqlAnalyseResults.ContainsKey(hashKey))
                    {
                        SqlAnalyseResult sqlAnalyseResult = new SqlAnalyseResult();

                        Counter tableIndexCounter = new Counter(0);
                        Counter outputColumnIndexCounter = new Counter(0);//注意在.net中输出的列是从0开始的
                        AnalyseInner(sqlProvider, entityType, sqlAnalyseResult, sqlAnalyseResult.ObjectFiller, taskPropertyNames, null, tableIndexCounter, outputColumnIndexCounter);

                        //添加到缓存
                        SqlAnalyseResults.Add(hashKey, sqlAnalyseResult);
                    }
                }
            }
            return SqlAnalyseResults[hashKey];
        }




        private static void AnalyseInner(
                ISqlProvider sqlProvider, EntityType entityType,
                SqlAnalyseResult sqlAnalyseResult, ObjectFiller objectFiller,
                IDictionary<string, SortedDictionary<GroupFunction, bool>> taskPropertyNames, string prefix,
                Counter tableIndexCounter, Counter outputColumnIndexCounter
                )
        {


            ITable table = sqlProvider.GetTableByEntityType(entityType);


            string currentTableNameAlias = "t" + tableIndexCounter.CurrentValue;
            if (prefix == null)
            {
                //前缀为null时表示是起始对象
                sqlAnalyseResult.MasterEntityType = entityType;
                sqlAnalyseResult.MasterTableNameAlias = currentTableNameAlias;
            }


            Dictionary<string, Dictionary<string, SortedDictionary<GroupFunction, bool>>> taskObjects = new Dictionary<string, Dictionary<string, SortedDictionary<GroupFunction, bool>>>(StringComparer.OrdinalIgnoreCase);
            objectFiller.PropertyFillers = new List<PropertyFiller>();
            foreach (string propertyName in taskPropertyNames.Keys)
            {
                PrefixParser prefixParser = new PrefixParser(propertyName);
                if (prefixParser.HasPrefix)
                {
                    //说明是外键对象的属性，例如"Person.City.CityId"
                    if (!taskObjects.ContainsKey(prefixParser.Prefix))
                    {
                        taskObjects.Add(prefixParser.Prefix, new Dictionary<string, SortedDictionary<GroupFunction, bool>>(StringComparer.OrdinalIgnoreCase));
                    }
                    taskObjects[prefixParser.Prefix].Add(prefixParser.PropertyName, taskPropertyNames[propertyName]);
                }
                else
                {
                    //说明是本对象的属性
                    string joinedPropertyName = PrefixParser.JoinPrefix(prefix, propertyName);


                    IColumn column = null;
                    if (joinedPropertyName == "***")
                    {
                        //伪造一个column
                        //column = sqlProvider.CreateCountColumn();
                    }
                    else
                    {
                        //如果不为***则column不会为null
                        if (!table.Columns.ContainsKey(propertyName))
                        {
                            throw new MissingMemberException(entityType.ToString(), propertyName);
                        }
                        column = table.Columns[propertyName];
                    }


                    foreach (KeyValuePair<GroupFunction, bool> kv in taskPropertyNames[propertyName])
                    {
                        if (!sqlAnalyseResult.HasGroup && kv.Key != GroupFunction.None)
                        {
                            sqlAnalyseResult.HasGroup = true;
                        }

                        //sqlColumn
                        SqlColumn sqlColumn = new SqlColumn();
                        sqlColumn.GroupFunction = kv.Key;
                        sqlColumn.FullPropertyName = joinedPropertyName;
                        sqlColumn.Column = column;


                        //oracle等数据库需要别名 
                        StringBuilder sb = new StringBuilder();
                        if (kv.Key != GroupFunction.None)
                        {
                            sb.Append(kv.Key.ToString());
                            sb.Append("(");
                        }
                        if (kv.Key == GroupFunction.Count)
                        {
                            sb.Append("*");
                        }
                        else
                        {
                            sb.Append(currentTableNameAlias);
                            sb.Append(".");
                            sb.Append(column.ColumnName);
                        }
                        if (kv.Key != GroupFunction.None)
                        {
                            sb.Append(")");
                        }
                        sqlColumn.TableAliasAndColumn = sb.ToString();


                        //filler
                        if (kv.Value)
                        {
                            //只有输出字段才有PropertyFiller
                            PropertyFiller propertyFiller = new PropertyFiller();
                            if (kv.Key == GroupFunction.None)
                            {
                                propertyFiller.PropertyAdapter = column.PropertyAdapter;
                            }
                            else
                            {
                                if (!table.ContainsGroupColumn(propertyName, kv.Key))
                                {
                                    throw new MissingMemberException(string.Format("类型{0}缺少标记为GroupFunction.{1}的GroupColumnAttribute", entityType, kv.Key));
                                }
                                propertyFiller.PropertyAdapter = table.GetGroupColumn(propertyName, kv.Key).PropertyAdapter;
                            }
                            propertyFiller.OutputColumnIndex = outputColumnIndexCounter.CurrentValue;
                            objectFiller.PropertyFillers.Add(propertyFiller);



                            sb.Append(" f");
                            sb.Append(outputColumnIndexCounter.CurrentValue);

                            OutputSqlColumn outputSqlColumn = new OutputSqlColumn();
                            outputSqlColumn.GroupFunction = kv.Key;
                            outputSqlColumn.TableAliasAndColumnAlias = sb.ToString();




                            outputColumnIndexCounter.AddOne();

                            sqlAnalyseResult.SortedOutputSqlColumns.Add(outputSqlColumn);
                        }


                        sqlAnalyseResult.AddSqlColumn(sqlColumn);
                    }
                }
            }


            //分析父对象，必须在自己属性分析完后才进行 
            objectFiller.ObjectFillers = new List<ObjectFiller>();
            foreach (string objectPeropertyName in taskObjects.Keys)
            {
                tableIndexCounter.AddOne();
                string tableNameAlias = "t" + tableIndexCounter.CurrentValue;


                if (!table.Relations.ContainsKey(objectPeropertyName))
                {
                    throw new MissingMemberException(entityType.ToString(), objectPeropertyName);
                }
                IRelation relation= table.Relations[objectPeropertyName];

                SqlTable sqlTable = new SqlTable();
                sqlTable.TableNameAlias = currentTableNameAlias;
                sqlTable.ForeignTableNameAlias = tableNameAlias;
                sqlTable.ForeignEntityType = relation.PropertyAdapter.PropertyType;


                List<SqlRelationColumn> sqlRelationColumns = new List<SqlRelationColumn>();
                foreach (IRelationColumn relationColumn in relation.RelationColumns)
                {
                    SqlRelationColumn sqlRelationColumn = new SqlRelationColumn();
                    sqlRelationColumn.ColumnName = relationColumn.ColumnName;
                    sqlRelationColumn.ForeignColumnName = relationColumn.ForeignColumnName;


                    StringBuilder sb = new StringBuilder();
                    sb.Append(sqlTable.TableNameAlias);
                    sb.Append('.');
                    sb.Append(relationColumn.ColumnName);
                    sb.Append('=');
                    sb.Append(sqlTable.ForeignTableNameAlias);
                    sb.Append('.');
                    sb.Append(relationColumn.ForeignColumnName);
                    sqlRelationColumn.Expression = sb.ToString();

                    sqlRelationColumns.Add(sqlRelationColumn);
                }
                sqlTable.RelationColumns = sqlRelationColumns;

                sqlAnalyseResult.ForeignTables.Add(sqlTable);




                ObjectFiller subObjectFiller = new ObjectFiller();
                subObjectFiller.PropertyAdapter = relation.PropertyAdapter;
                objectFiller.ObjectFillers.Add(subObjectFiller);

                //AnalyseInner(sqlProvider, relation.PropertyAdapter.PropertyType, sqlAnalyseResult, subObjectFiller,taskObjects[objectPeropertyName], objectPeropertyName, tableIndexCounter, outputColumnIndexCounter);

                string joinedPropertyName = PrefixParser.JoinPrefix(prefix, objectPeropertyName);
                AnalyseInner(sqlProvider, relation.PropertyAdapter.PropertyType, sqlAnalyseResult, subObjectFiller, taskObjects[objectPeropertyName], joinedPropertyName, tableIndexCounter, outputColumnIndexCounter);

            }

        }


        #region CollectPropertyNames



        private static void CollectPropertyNames(SortedDictionary<string, SortedDictionary<GroupFunction, bool>> taskPropertyNames, IPropertyExpressionCollection selects, ISqlProvider sqlProvider, EntityType entityType)
        {
            if (selects != null)
            {
                foreach (IPropertyExpression obj in selects)
                {
                    if (obj.GroupFunction == GroupFunction.None && obj.PropertyName.EndsWith("*"))
                    {
                        //需要支持多个"Area.*"这种模式
                        string prefix = "";
                        string[] ss = obj.PropertyName.Split('.');
                        int start = 0;

                        ITable table = sqlProvider.GetTableByEntityType(entityType);
                        while (start < ss.Length - 1)
                        {
                            EntityType relationType = table.Relations[ss[start]].PropertyAdapter.PropertyType;
                            table = sqlProvider.GetTableByEntityType(relationType);
                            prefix = prefix + ss[start] + ".";
                            start++;
                        }
                        foreach (string s in table.Columns.Keys)
                        {
                            string joined = prefix + s;
                            if (!taskPropertyNames.ContainsKey(joined))
                            {
                                taskPropertyNames.Add(joined, new SortedDictionary<GroupFunction, bool>());//需要输出，所以值为true
                            }
                            if (!taskPropertyNames[joined].ContainsKey(GroupFunction.None))
                            {
                                taskPropertyNames[joined].Add(GroupFunction.None, true);//不需要输出，所以值为false
                            }
                        }
                    }
                    else
                    {
                        string propertyName = obj.GroupFunction == GroupFunction.Count ? "***" : obj.PropertyName;
                        if (!taskPropertyNames.ContainsKey(propertyName))
                        {
                            taskPropertyNames.Add(propertyName, new SortedDictionary<GroupFunction, bool>());//需要输出，所以值为true
                        }
                        if (!taskPropertyNames[propertyName].ContainsKey(obj.GroupFunction))
                        {
                            taskPropertyNames[propertyName].Add(obj.GroupFunction, true);//不需要输出，所以值为false
                        }
                    }
                }
            }
        }


        private static void CollectPropertyNames(SortedDictionary<string, SortedDictionary<GroupFunction, bool>> taskPropertyNames, IGroupPropertyExpressionCollection groups)
        {
            
            if (groups != null)
            {
                foreach (IGroupPropertyExpression obj in groups)
                { 
                    if (!taskPropertyNames.ContainsKey(obj.PropertyName))
                    {
                        taskPropertyNames.Add(obj.PropertyName, new SortedDictionary<GroupFunction, bool>());//不需要输出，由于总是OutputExpression.Selects先分析，所以这里可以加入null
                    }
                    if (!taskPropertyNames[obj.PropertyName].ContainsKey(GroupFunction.None))
                    {
                        taskPropertyNames[obj.PropertyName].Add(GroupFunction.None, false);//不需要输出，所以值为false
                    }
                }
            }
        }
        private static void CollectPropertyNames(SortedDictionary<string, SortedDictionary<GroupFunction, bool>> taskPropertyNames, INameValueExpressionCollection nameValues)
        {
            if (nameValues != null)
            {
                foreach (INameValueExpression obj in nameValues)
                {
                    if (!taskPropertyNames.ContainsKey(obj.PropertyName))
                    {
                        taskPropertyNames.Add(obj.PropertyName, new SortedDictionary<GroupFunction, bool>());//不需要输出，由于总是OutputExpression.Selects先分析，所以这里可以加入null
                    }
                    if (!taskPropertyNames[obj.PropertyName].ContainsKey(GroupFunction.None))
                    {
                        taskPropertyNames[obj.PropertyName].Add(GroupFunction.None, false);//不需要输出，所以值为false
                    }
                }
            }
        }

        private static void CollectPropertyNames(SortedDictionary<string, SortedDictionary<GroupFunction, bool>> taskPropertyNames, IOrderExpressionCollection orders)
        {
            if (orders != null)
            {
                foreach (IOrderExpression obj in orders)
                {
                    if (!taskPropertyNames.ContainsKey(obj.PropertyName))
                    {
                        taskPropertyNames.Add(obj.PropertyName, new SortedDictionary<GroupFunction, bool>());
                    }
                    if (!taskPropertyNames[obj.PropertyName].ContainsKey(obj.GroupFunction))
                    {
                        taskPropertyNames[obj.PropertyName].Add(obj.GroupFunction, false);//不需要输出，所以值为false
                    }
                }
            }
        }
        private static void CollectPropertyNames(SortedDictionary<string, SortedDictionary<GroupFunction, bool>> taskPropertyNames, IConditionExpression wheres)
        {
            if (wheres != null)
            {
                if (wheres is IConditionCollection)
                {
                    IConditionCollection conditionCollection = (IConditionCollection)wheres;
                    foreach (ICondition obj in conditionCollection)
                    {
                        CollectPropertyNames(taskPropertyNames, obj);
                    }
                }
                else
                {
                    IConditionItem conditionItem = (IConditionItem)wheres;
                    if (!taskPropertyNames.ContainsKey(conditionItem.PropertyName))
                    {
                        taskPropertyNames.Add(conditionItem.PropertyName, new SortedDictionary<GroupFunction, bool>());
                    }
                    if (!taskPropertyNames[conditionItem.PropertyName].ContainsKey(conditionItem.GroupFunction))
                    {
                        taskPropertyNames[conditionItem.PropertyName].Add(conditionItem.GroupFunction, false);//不需要输出，所以值为false
                    }
                }
            }
        }


        #endregion




        private static string GetHashKey(EntityType entityType, SortedDictionary<string, SortedDictionary<GroupFunction, bool>> allPropertyNames)
        {
            //使用sorted是为了判断gethashcode时是否缓存中已经存在，
            //避免字段都相同而顺序不同造成的错误判断
            StringBuilder sb = new StringBuilder();
            sb.Append(entityType); //注意不同实体类型可能有相同的字段因此必须传入entityType
            sb.Append(':');
            foreach (string s in allPropertyNames.Keys)
            {
                sb.Append(s);
                if (allPropertyNames[s]!=null)
                {
                    foreach (KeyValuePair<GroupFunction, bool> kv in allPropertyNames[s])
                    {
                        sb.Append(",");
                        sb.Append(kv.Key);
                        sb.Append(kv.Value);
                    }
                }
                sb.Append(';');
            }
            return sb.ToString();
        }



    }
}
